mod powerful_viewcube;
mod simple_viewcube;

use bevy::app::PostStartup;
use bevy::color::Color;
use bevy::math::Mat3;
use bevy::pbr::{DirectionalLight, StandardMaterial};
use bevy::prelude::*;
use bevy::{
    app::{Plugin, Startup, Update},
    ecs::{
        component::Component,
        entity::Entity,
        query::With,
        system::{Commands, ParamSet, Query},
    },
    math::{UVec2, Vec3},
    prelude::default,
    render::camera::Camera,
    transform::components::Transform,
    window::Window,
};
// use bevy_mod_outline::OutlineVolume;
#[cfg(feature = "panorbit")]
use bevy_panorbit_camera::PanOrbitCamera;
use powerful_viewcube::CubeMaterial;
use std::f32::consts::PI;
use std::sync::LazyLock;

use crate::{SmallView, ViewcubeBinding, ViewcubeFocusCenter, PI_2, PI_4, PI_4_3};

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub(crate) enum CubePart {
    // Face
    Front,
    Back,
    Left,
    Right,
    Top,
    Bottom,
    // Edge
    FrontTop,
    FrontBottom,
    BackTop,
    BackBottom,
    LeftTop,
    LeftBottom,
    RightTop,
    RightBottom,
    FrontLeft,
    FrontRight,
    BackLeft,
    BackRight,
    // Corner
    FrontLeftTop,
    FrontLeftBottom,
    FrontRightTop,
    FrontRightBottom,
    BackLeftTop,
    BackLeftBottom,
    BackRightTop,
    BackRightBottom,
}

#[derive(Default)]
pub struct BevyViewCubePlugin {
    pub use_powerful_viewcube: bool,
}

impl Plugin for BevyViewCubePlugin {
    fn build(&self, app: &mut bevy::prelude::App) {
        let setup = if self.use_powerful_viewcube {
            powerful_viewcube::setup
        } else {
            simple_viewcube::setup
        };
        app.init_resource::<CubeMaterial>();
        // app.add_plugins(MeshPickingPlugin);
        // app.add_plugins(bevy_mod_outline::OutlinePlugin);
        app.add_systems(PostStartup, (setup, crate::create_small_view).chain())
            .add_systems(Update, update_view)
            .add_systems(Update, viewcube_hit)
            .add_systems(Update, face_hovered)
            .insert_resource(ViewcubeFocusCenter(Vec3::ZERO));
    }
}

#[derive(Component)]
pub(crate) struct ViewcubeCenter;

#[derive(Component, Debug)]
pub(crate) struct FaceHovered;
#[derive(Component, Debug, Copy, Clone)]
pub(crate) struct ViewcubeHit(pub CubePart);

#[derive(Component, Debug)]
pub struct MainFace;

#[macro_export]
macro_rules! generate_corner_face {
     ($builder:ident, $meshes:ident, $materials: ident, $part: expr, $transform: expr, $component: expr) => {
        let mut ec: EntityCommands = $builder.spawn((
            Mesh3d($meshes.add($part.clone())),
            MeshMaterial3d($materials.clone()),
            $transform,
            RenderLayers::layer(13),
        ));
        ec.observe(crate::viewcube::on_click_spawn_cube::<Pointer<Click>>($component));
    };
}

// fn on_click_spawn_cube(
//     click: Trigger<Pointer<Click>>,
//     mut commands: Commands,
// ) {
//     if event.button == bevy::prelude::PointerButton::Left && event.state.is_pressed() {
//         commands.entity(event.target).insert($ component);
//     }
// }

pub fn on_click_spawn_cube<E: Event>(
    hit: ViewcubeHit
) -> impl Fn(Trigger<E>, Commands) {
    move |trigger, mut commands| {
        commands.entity(trigger.target().unwrap()).insert(hit);
    }
}

// On::<Pointer<Click>>::commands_mut(|event, commands| {
// if event.button == bevy::prelude::PointerButton::Left && event.state.is_pressed() {
// commands.entity(event.target).insert($component);
// }
// }),
// On::<Pointer<Click>>::commands_mut(|event, commands| {
// if event.button == bevy::prelude::PointerButton::Left && event.state.is_hovered() {
// commands.entity(event.target).insert(crate::viewcube::FaceHovered);
// }
// }),

#[macro_export]
macro_rules! generate_main_face {
    ($builder:ident, $meshes:ident, $materials: ident, $part: expr, $transform: expr, $component: expr) => {
        $builder.spawn((
            Mesh3d($meshes.add($part.clone())),
            MeshMaterial3d($materials.clone()),
            $transform,
            RenderLayers::layer(13),
            crate::viewcube::MainFace,
        )).observe(crate::viewcube::on_click_spawn_cube::<Pointer<Click>>($component));
    };
}

pub(crate) fn update_view(
    windows: Query<&Window>,
    mut camera_set: ParamSet<(
        Query<&mut Camera, With<crate::SmallView>>,
        Query<&Camera, With<ViewcubeBinding>>,
    )>,
    mut transform_query: ParamSet<(
        Query<&mut Transform, With<ViewcubeCenter>>,
        Query<&Transform, With<crate::ViewcubeBinding>>,
        Query<&mut Transform, (With<DirectionalLight>, With<SmallView>)>,
    )>,
) {
    let Ok(window) = windows.single() else {
        return;
    };
    let rect = {
        let binding = camera_set.p1();
        let main_cam = binding.single().unwrap();
        main_cam.physical_viewport_rect().unwrap()
    };
    let mut binding = camera_set.p0();
    let mut small_cam = binding.single_mut().unwrap();

    const VIEWCUBE_SIZE: u32 = 180;
    if rect.width() < VIEWCUBE_SIZE || rect.height() < VIEWCUBE_SIZE {
        return;
    }
    small_cam.viewport = Some(bevy::render::camera::Viewport {
        physical_position: rect.min + UVec2::new(rect.width() - VIEWCUBE_SIZE, 0),
        physical_size: UVec2::new(VIEWCUBE_SIZE, VIEWCUBE_SIZE),
        ..default()
    });
    let transform;
    {
        let orbit_cameras = transform_query.p1();
        transform = if let Ok(tr) = orbit_cameras.single() {
            tr.clone()
        } else {
            Transform::IDENTITY
        }
    }

    {
        let mut trident = transform_query.p0();
        let mut trident_transform = trident.single_mut().unwrap();
        trident_transform.rotation = transform.rotation.inverse();
    }

    //light camera也要同步
    // let mut light_cam = transform_query.p2();
    // light_cam.single_mut().rotation = transform.rotation;
}

pub(crate) fn viewcube_hit(
    mut commands: Commands,
    mut entity: Query<(Entity, &ViewcubeHit)>,
    mut query: Query<&mut Transform, With<ViewcubeBinding>>,
    view_cube_center: Res<ViewcubeFocusCenter>,
    #[cfg(feature = "panorbit")] mut camera: Query<&mut PanOrbitCamera>,
) {
    if entity.is_empty() {
        return;
    }
    let (item, hit) = entity.single().unwrap();
    commands.entity(item).remove::<ViewcubeHit>();
    commands.entity(item).remove::<FaceHovered>();
    // outline.visible = false;

    // dbg!(dir.0);
    let (alpha, beta) = match hit.0 {
        CubePart::Right => (PI_2, 0.0),
        CubePart::Left => (-PI_2, 0.0),
        CubePart::Top => (0.0, PI_2),
        CubePart::Bottom => (0.0, -PI_2),
        CubePart::Front => (0.0, 0.0),
        CubePart::Back => (crate::PI, 0.0),
        CubePart::FrontTop => (0.0, PI_4),
        CubePart::FrontBottom => (0.0, -PI_4),
        CubePart::BackTop => (crate::PI, PI_4),
        CubePart::BackBottom => (crate::PI, -PI_4),
        CubePart::LeftTop => (-PI_2, PI_4),
        CubePart::LeftBottom => (-PI_2, -PI_4),
        CubePart::RightTop => (PI_2, PI_4),
        CubePart::RightBottom => (PI_2, -PI_4),
        CubePart::FrontLeft => (-PI_4, 0.0),
        CubePart::FrontRight => (PI_4, 0.0),
        CubePart::BackLeft => (-PI_4_3, 0.0),
        CubePart::BackRight => (PI_4_3, 0.0),
        CubePart::FrontLeftTop => (-PI_4, PI_4),
        CubePart::FrontLeftBottom => (-PI_4, -PI_4),
        CubePart::FrontRightTop => (PI_4, PI_4),
        CubePart::FrontRightBottom => (PI_4, -PI_4),
        CubePart::BackLeftTop => (-PI_4_3, PI_4),
        CubePart::BackLeftBottom => (-PI_4_3, -PI_4),
        CubePart::BackRightTop => (PI_4_3, PI_4),
        CubePart::BackRightBottom => (PI_4_3, -PI_4),
    };

    for mut transform in query.iter_mut() {
        //根据当前的视角来调整摄像头方向
        transform.rotation = Quat::from_rotation_y(alpha) * Quat::from_rotation_x(-beta);
        let radius = (transform.translation - view_cube_center.0).length();
        transform.translation =
            view_cube_center.0 + transform.rotation * Vec3::new(0.0, 0.0, radius);
    }

    #[cfg(feature = "panorbit")]
    {
        let mut orbit_camera = camera.single_mut();

        // orbit_camera.target_focus = Vec3::ZERO;
        orbit_camera.target_yaw = alpha;
        orbit_camera.target_pitch = beta;
    }
}

fn face_hovered(
    mut commands: Commands,
    mut query: Query<(Entity, Option<&MainFace>, &FaceHovered), Changed<FaceHovered>>,
    mut last_hovered: Local<Option<Entity>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
    cube_material: Res<CubeMaterial>,
    mut query_handles: Query<(Option<&MainFace>, &MeshMaterial3d<StandardMaterial>)>,
) {
    for (entity, main_face, _) in &mut query {
        // Reset previous material color if it exists
        if let Some(last_entity) = *last_hovered {
            if let Ok((old_main_face, old_mh)) = query_handles.get(last_entity) {
                if old_main_face.is_some() {
                    if let Some(material) = materials.get_mut(old_mh) {
                        material.base_color = Color::WHITE;
                    }
                } else {
                    commands
                        .entity(last_entity)
                        .insert(MeshMaterial3d(cube_material.blank_material.clone()));
                }
            }
        }

        // Set new material color and store entity
        if let Ok((main_face, material_handle)) = query_handles.get(entity) {
            // dbg!(main_face.is_some());
            if main_face.is_some() {
                if let Some(material) = materials.get_mut(material_handle) {
                    material.base_color = Color::srgba(0.7, 0.9, 1.0, 1.0); // Light blue
                }
            } else {
                // dbg!("corner edge hover");
                commands
                    .entity(entity)
                    .insert(MeshMaterial3d(cube_material.corner_edge_hover.clone()));
            }
        }
        *last_hovered = Some(entity);
    }
}
